/* Authors: Tyler Wozniak
*			Andrew Hall
*  Tic Tac Toe HW 1
*
*/

#include <iostream>
using namespace std;

#define MALLOC 1 //1 for compiling malloc version, 0 for compiling array version

#pragma region Fields

const int BOARD_SIZE = 3;	//size of the board both vertically and horizontally
#if !MALLOC
char** cArray;				//2D array storing the board
#elif MALLOC
char** cMalloc;				//Char-pointer-pointer for the malloc'd 2D array
#endif
#pragma endregion

#pragma region Function Prototypes
void init();

#if !MALLOC
void arrayTicTacToe();
bool getHorizontalArr();
bool getVerticalArr();
bool getDiagonalArr();
void clearArray();
void printArray();
bool arrayWin();

#elif MALLOC
void mallocTicTacToe();
bool getHorizontalMal();
bool getVerticalMal();
bool getDiagonalMal();
void clearMalloc();
void printMalloc();
bool mallocWin();
#endif
#pragma endregion

//Runs the appropriate function for the game, and then waits for user input before ending
int main(){

#if !MALLOC
	arrayTicTacToe();
#elif MALLOC
	mallocTicTacToe();
#endif

	system("PAUSE");
}

//init creates the array/memory allocation & gives a default value to the board: '_'
void init(){
#if !MALLOC
	//loops through the board 'cArray' and initializes each space to be an underscore
		cArray = new char*[BOARD_SIZE];
		for(int i = 0; i < BOARD_SIZE; i++)
			cArray[i] = new char[BOARD_SIZE];

		for(int i = 0; i < BOARD_SIZE; i++) {	
			for(int j = 0; j < BOARD_SIZE; j++) {
				cArray[i][j] = '_';
			}
		}
#elif MALLOC
		// Allocates a pointer to 3 char pointers, which are the rows
		cMalloc = (char**) malloc(BOARD_SIZE*sizeof(char*));
		for(int i = 0; i < BOARD_SIZE; i++)
		{
			//Allocates each row as 3 char pointers
			*(cMalloc + i) = (char*) malloc(BOARD_SIZE*sizeof(char));
		}

		for (int x = 0; x < BOARD_SIZE; x++)
		{
			for (int y = 0; y < BOARD_SIZE; y++)
			{
				* ( *(cMalloc + x)+y) = '_';
			}
		}
#endif
}

#pragma region Array Functions
#if !MALLOC
/* Starts by running the init function and clearing the array again (just in case)
 * Then sets win bool to false and runs through the gameplay logic, letting the player
 * select the column and row to place his letter. Checks win condition and finishes if that's
 * the case.
 */
void arrayTicTacToe(){
	init();
	clearArray();
	bool win = false; // bool for win condition
	int counter = 0; //Counter - helps determine when the board is full for a tie
	int currPlayer; //Current player (1 or 2)
	do{
		printArray();
		int y;
		int x;
		currPlayer = counter%2 + 1;
		cout << "Player " << currPlayer << "'s turn.  Please select the row and column to place your letter." << endl;
		
		do{	// Waits for an int input within bounds
			cout << "Column: ";
			cin >> x;
			x--;
			if(x > 2 || x < 0)
				cout << "Please choose a number between 1 and 3" << endl;
		}while(x > 2 || x < 0);

		do{ // Waits for an int input within bounds
			cout << "Row: ";
			cin >> y;
			y--;
			if(y > 2 || y < 0)
				cout << "Please choose a number between 1 and 3" << endl;
		}while(y > 2 || y < 0);


		if(cArray[y][x] != '_')
			cout << "Error: You picked an invalid space. Please pick an empty space." << endl; // Error if you pick a space that's already been picked
		else{
			counter++;
			if(currPlayer == 1)
				cArray[y][x] = 'X';
			else
				cArray[y][x] = 'O';
		}

		win = arrayWin();
	}while(!win && counter < 9);

	printArray();
	if(win)
		cout << "Hey, look! Player " << currPlayer << " wins!" << endl;
	else
		cout << "Too bad, it ended in a tie... :/" << endl;

	//Waste management here
	for(int i = 0; i < BOARD_SIZE; i++)
	{
		delete[] cArray[i]; 
	}
	delete[] cArray;
}

//Resets the array to all underscores
void clearArray(){
	 for(int i = 0; i < BOARD_SIZE; i++){
		 for(int j = 0; j < BOARD_SIZE; j++){
			 cArray[i][j]='_';
		 }
	 }
}

//Prints the array on the screen
void printArray(){
	system("CLS");
	for(int i = 0; i < BOARD_SIZE; i++){
		 for(int j = 0; j < BOARD_SIZE; j++){
			cout << "|" << cArray[i][j];
		 }
		 cout << "|" << endl;
	 }
}

//Checks the win condition based on the three helper functions
bool arrayWin(){
	return getHorizontalArr() || getVerticalArr() || getDiagonalArr();
			
}

//Checks for horizontal winning rows
bool getHorizontalArr(){
	char tmp;
	for(int i = 0; i < BOARD_SIZE; i++)
	{
		tmp = cArray[i][0];
		if(cArray[i][1] == tmp && cArray[i][2] == tmp && tmp != '_')
			return true;
	}
	return false;
}

//Checks for vertical winning rows
bool getVerticalArr(){
	char tmp;
	for(int i = 0; i < BOARD_SIZE; i++)
	{
		tmp = cArray[0][i];
		if(cArray[1][i] == tmp && cArray[2][i] == tmp && tmp != '_')
			return true;
	}
	return false;
}

//Checks for diagonal winning rows
bool getDiagonalArr(){
	char tmp;
	tmp = cArray[1][1];
	if(((cArray[0][0] == tmp && cArray[2][2] == tmp) || (cArray[0][2] == tmp && cArray[2][0] == tmp)) && tmp != '_')
		return true;
	return false;
}

#endif
#pragma endregion

#pragma region Malloc Functions
#if MALLOC
/* Starts by running the init function and clearing the array again (just in case)
 * Then sets win bool to false and runs through the gameplay logic, letting the player
 * select the column and row to place his letter. Checks win condition and finishes if that's
 * the case.
 */
void mallocTicTacToe(){
	init();
	clearMalloc();
	bool win = false; //bool for win condition
	int counter = 0; //Counter for checking to see if board is full for ties
	int currPlayer; //Current player (1 or 2)
	do{

		printMalloc();
		int y=0;
		int x=0;
		currPlayer = counter%2 + 1;
		cout << "Player " << currPlayer << "'s turn.  Please select the row and column to place your letter." << endl;
		
		do{ //Waits for input within bounds
			cout << "Column: ";
			cin >> x;
			x--;
			if(x > 2 || x < 0)
				cout << "Please choose a number between 1 and 3" << endl;
		}while(x > 2 || x < 0);

		do{ //Waits for input within bounds
			cout << "Row: ";
			cin >> y;
			y--;
			if(y > 2 || y < 0)
				cout << "Please choose a number between 1 and 3" << endl;
		}while(y > 2 || y < 0);			

		if(*(*(cMalloc + y) + x) != '_')
			cout << "Error: You picked an invalid space. Please pick an empty space." << endl; //Error if you picked a space that was already picked

		else{

			counter++;
			if(currPlayer == 1)
				*(*(cMalloc + y) + x) = 'X';
			else
				*(*(cMalloc + y) + x) = 'O';
		}

		win = mallocWin();
	}while(!win && counter < 9);

	printMalloc();
	if(win)
		cout << "Hey, look! Player " << currPlayer << " wins!" << endl;
	else
		cout << "Too bad, it ended in a tie... :/" << endl;

	//Waste management here
	for(int i = 0; i < BOARD_SIZE; i++)
	{
		free(*(cMalloc + i)); 
	}
	free(cMalloc);
}

//Resets the board to underscores
void clearMalloc(){
	for (int x = 0; x < BOARD_SIZE; x++)
	{
		for (int y = 0; y < BOARD_SIZE; y++)
		{
			* ( *(cMalloc + x)+y) = '_';
		}
	}
}

//Prints the board
void printMalloc(){
	system("CLS");
	for (int x = 0; x < BOARD_SIZE; x++)
	{
		cout << '|';
		for (int y = 0; y < BOARD_SIZE; y++)
		{
			cout << * ( *(cMalloc + x)+y) << '|';
		}
		cout << endl;
	}

}

//Checks for win using 3 helper functions
bool mallocWin(){
	return getHorizontalMal() || getVerticalMal() || getDiagonalMal();
}

//Checks for horizontal wins
bool getHorizontalMal(){
	char tmp;
	for(int i = 0; i < BOARD_SIZE; i++)
	{
		tmp = *(*(cMalloc + i));
		if(*(*(cMalloc + i) + 1) == tmp && *(*(cMalloc + i) + 2) == tmp && tmp != '_')
			return true;
	}
	return false;
}

//Checks for vertical wins
bool getVerticalMal(){
	char tmp;
	for(int i = 0; i < BOARD_SIZE; i++)
	{
		tmp = *(*(cMalloc) + i);
		if(*(*(cMalloc + 1) + i) == tmp && *(*(cMalloc + 2) + i) == tmp && tmp != '_')
			return true;
	}
	return false;
}

//Checks for diagonal wins
bool getDiagonalMal(){
	char tmp;
	tmp = *(*(cMalloc + 1) + 1);
	if(((*(*(cMalloc)) == tmp && *(*(cMalloc + 2) + 2) == tmp) || (*(*(cMalloc) + 2) == tmp && *(*(cMalloc + 2)) == tmp)) && tmp != '_')
		return true;
	return false;
}

#endif
#pragma endregion